/*
 * Copyright 2010 Michael Nitsche
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.mnit.basis.daten.struktur.schluessel.topf;

import de.mnit.basis.daten.WandleObjekt;
import de.mnit.basis.daten.struktur.liste.Liste;
import de.mnit.basis.daten.struktur.liste.S_Liste;
import de.mnit.basis.daten.struktur.menge.Menge;
import de.mnit.basis.daten.struktur.menge.S_Menge;
import de.mnit.basis.fehler.Fehler;
import de.mnit.basis.sys.Ausgabe;


/**
 * @author Michael Nitsche
 */
public class SchluesselTopf<TA,TB> extends A_SchluesselTopf_Basis<TA,TB> implements S_SchluesselTopf<TA,TB> {

	private S_Menge<S_Liste<TB>> toepfe;


	public SchluesselTopf() {
		super();
		this.toepfe=Menge.neu();
	}


	public static final <TA,TB> SchluesselTopf<TA,TB> neu() {
		return new SchluesselTopf<TA,TB>();
	}

	public static final <TA,TB> SchluesselTopf<TA,TB> neu(Class<TA> a, Class<TB> b) {
		return neu();
	}


	public S_Liste<TB> gib(TA schluessel) {
		Integer pos=this.schluessel.gPosition(schluessel);
		Fehler.objekt.wenn_Null(pos);
		return this.toepfe.gib(pos);
	}

	public S_Liste<TB> gib(TA schluessel, S_Liste<TB> wenn_fehlt) {
		Integer pos=this.schluessel.gPosition(schluessel);
		if(pos==null) return wenn_fehlt;
		return this.toepfe.gib(pos);
	}

	public S_SchluesselTopf<TA,TB> _plusAlle(TA schluessel, TB... objekt) {
		for(TB o : objekt) this.plus(schluessel,o);
		return this;
	}

	public void plus(TA schluessel, TB objekt) {
		Integer pos=this.schluessel.gPosition(schluessel);
		if(pos==null) {
			this.schluessel.plus(schluessel);
			S_Liste<TB> neu=Liste.neu();
			toepfe.plus(neu);
			pos=this.schluessel.gPosition(schluessel);
		}
		this.toepfe.gib(pos).plus(objekt);
	}

	public void minus(TA schluessel) {
		Integer pos=this.schluessel.gPosition(schluessel);
		if(pos==null)
			Fehler.objekt.da_HierUngueltig(schluessel,"Schlüssel ungültig");
		this.schluessel.minus(pos);
		this.toepfe.minus(pos);
	}

	public TB minus(TA schluessel, TB objekt) {
		Integer pos=this.schluessel.gPosition(schluessel);
		if(pos==null)
			Fehler.objekt.da_HierUngueltig(schluessel,"Schlüssel ungültig");
		S_Liste<TB> topf=this.toepfe.gib(pos);
		if(topf.kennt(objekt))
			topf.minus(objekt);
		else
			Fehler.objekt.da_HierUngueltig(objekt,"Objekt nicht im Topf");
		return objekt;
	}

	public S_Liste<TB> minusAlle(TA schluessel) {
		Integer pos=this.schluessel.gPosition(schluessel);
		if(pos==null)
			Fehler.objekt.da_HierUngueltig(schluessel,"Schlüssel ungültig");
		S_Liste<TB> erg=this.toepfe.gib(pos).kopie();
		while(this.toepfe.gib(pos).gLaenge()>0)
			this.toepfe.gib(pos).minus(1);
		return erg;
	}

	public String toString() {
		StringBuilder sb=new StringBuilder();
		for(int pos=1; pos<=this.gLaenge(); pos++) {
			sb.append(WandleObjekt.zuText(this.gSchluessel().gib(pos))+": ");
			sb.append(WandleObjekt.zuText(this.gib(2, pos))+"\n");
		}
		return sb.toString();
	}

	public void vertauschen(int zeile_a, int zeile_b) {
		Fehler.zahl.wenn_InListe(zeile_a,0);
		Fehler.zahl.wenn_InListe(zeile_b,0);
		Fehler.zahl.wenn_Ausserhalb(1, gLaenge(), Math.abs(zeile_a));
		Fehler.zahl.wenn_Ausserhalb(1, gLaenge(), Math.abs(zeile_b));

		if( zeile_a == zeile_b ) {
			Ausgabe.warnung("Vertauschen von "+(zeile_a)+" und "+(zeile_b)+" nicht nötig!");
			return;
		}

		this.schluessel.vertauschen(zeile_a, zeile_b);
		this.toepfe.vertauschen(zeile_a, zeile_b);
	}

	public void sortiereSo(int[] so) {
		this.schluessel.sortieren(so);
		this.toepfe.sortieren(so);
	}

	public Object gib(int spalte, int zeile) {
		if(spalte==1)
			return this.schluessel.gib(zeile);
		else if(spalte==2)
			return this.toepfe.gib(zeile);
		else throw Fehler.zahl.da_Ungueltig(spalte);
	}

	public void ausgeben() {
		Ausgabe.debug(this);
	}

	public S_SchluesselTopf<TA, TB> kopie() {
		throw Fehler.da_ToDo();
	}

}
